﻿package  com.hadron.mp3player
{
	import br.com.stimuli.loading.BulkProgressEvent;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.MouseEvent;
	import flash.filters.BlurFilter;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.AntiAliasType;
	import flash.net.URLRequest;
	import br.com.stimuli.loading.BulkLoader;
	import br.com.stimuli.loading.BulkProgressEvent;
	import com.hadron.mp3player.*;
	import com.hadron.graphics.DrawRectangle;
	import com.caurina.transitions.Tweener;
	import com.caurina.transitions.properties.SoundShortcuts;
	import com.everydayflash.equalizer.*;
	import com.everydayflash.equalizer.color.*;
	
	import nl.demonsters.debugger.MonsterDebugger;
	import flash.external.ExternalInterface;
	
	/**
	 * ...
	 * @author Hadron
	 */
	
	
	public class MP3Player extends Sprite
	{
		
		private var playerData:XML;
		private var loader:BulkLoader;
		private var sound:Sound;
		private var isPlaying:Boolean = false;
		private var soundControl:SoundChannel = new SoundChannel();
		private var transf:SoundTransform = new SoundTransform(.5, 0);
		private var curTransform:SoundTransform;
		private var soundURL:URLRequest = new URLRequest();
		private var resumeTime:Number = 0;
		private var equalizer:Equalizer;
		
		public function MP3Player(playerData:XML) 
		{
			SoundShortcuts.init(); 
			this.playerData = playerData;
			loader = BulkLoader.getLoader('main_loader');
			
			var es:EqualizerSettings = new EqualizerSettings();
			es.numOfBars = 32;
			es.height = 20;
			es.barSize = 2;
			es.vgrid = true;
			es.hgrid = 2;
	
			es.colorManager = new SolidBarColor(0xFFD9CEB4); // remember ARGB seeting! 
			
			equalizer = new Equalizer();
			equalizer.update(es);
			equalizer.y -= 20;
			addChild(equalizer);
			
			soundURL.url = playerData.item[0].@path;
			createButtons();
			
			//var mdb:MonsterDebugger = new MonsterDebugger(this);
			if (ExternalInterface.available)
			{
				ExternalInterface.addCallback("stopPlaying", receivedFromJavaScript);
			}
			
			//MonsterDebugger.trace(this, 'Jestem qna!');
		}
		
		private function receivedFromJavaScript():void
		{
			pauseSound();
			//MonsterDebugger.trace(this, "MP3 stop playing");
		}
		
		//----------------------------------------- LOADING MANAGEMENT ------------------------------------------------//
		public function loadSound():void
		{
			// NEW ITEM TO LOAD //
			//MonsterDebugger.trace(this, 'Item Loading: ' + loader.get(soundURL.url));
			if(loader.get(soundURL.url) == null) 
			{
				//MonsterDebugger.trace(this, 'loading Sound first time');
				showPreloader();
				loader.add(soundURL.url);
				loader.get(soundURL.url).addEventListener(BulkLoader.COMPLETE, onSoundComplete, false, 0, true);
				if (!loader.hasEventListener(BulkProgressEvent.PROGRESS))
				{
					//MonsterDebugger.trace(this, 'It has no progressHandler');
					loader.addEventListener(BulkProgressEvent.PROGRESS, onSoundProgress, false, 0, true);
				}
				loader.start();
			}
			// ITEM LOADED ONCE //
			else 
			{
				MonsterDebugger.trace(this, 'Sound loaded once!');
				validateSound();
			}
		}
		
		private function validateSound():void
		{
			//MonsterDebugger.trace(this, 'validating sound');
			sound = new Sound(soundURL);
			resumeTime = 0;
			soundControl = sound.play(resumeTime);
			soundControl.soundTransform = transf;
			soundControl.addEventListener(Event.SOUND_COMPLETE, onSoundEnd, false, 0, true);
			enableVolumeControls();
			isPlaying = true;
			if (!hasEventListener(Event.ENTER_FRAME))
			{
				addEventListener(Event.ENTER_FRAME, onEnterFrame, false, 0, true);
			}
		}
		
		private function onSoundProgress(e:BulkProgressEvent):void
		{
			//MonsterDebugger.trace(this, 'Percent loaded: ' + e.percentLoaded);
			if ((e.percentLoaded > .2) && (sound == null))
			//if (e.percentLoaded > .2)
			{
				//MonsterDebugger.trace(this, "I've got more than 20% - creating sound object!");
				validateSound();
			}
		}
		
		private function onSoundComplete(e:Event):void
		{
			removePreloader();
			// remove progress listener
			//MonsterDebugger.trace(this, 'Finished downloading: ' + e.type);
			loader.get(soundURL.url).removeEventListener(BulkLoader.COMPLETE, onSoundComplete);
			loader.removeEventListener(BulkProgressEvent.PROGRESS, onSoundProgress);
		}
		
		//------------------------------------------------ TRACK MANAGEMENT ---------------------------------------------------//
		private function onSoundEnd(e:Event):void
		{
			disableVolumeControls();
			sound = null;
			
			if (trackList.selectedIndex < (trackList.length - 1))
			{
				// select next item of the list
				trackList.selectedItem = trackList.getItemAt(trackList.selectedIndex + 1);
			}
			else
			{
				trackList.selectedItem = trackList.getItemAt(0);
			}
			
			soundURL.url = trackList.selectedItem.data;
			loadSound();
		}
		
		private function onTrackChange(e:Event):void
		{
			trace(loader.isRunning);
			//trace('SelectedItem:' + e.target.selectedItem.data);
			//disable & fade volume buttons only if Sound is not buffering
		
			disableVolumeControls();
				
			// Tween current sound to volume 0 
			Tweener.addTween(soundControl, { _sound_volume:0, time:.5, transition:"linear", onComplete:
				function():void
				{
					// Stop current sound
					soundControl.stop();
					isPlaying = false;
					
					//null the sound instance
					sound = null;
					
					// load new url
					soundURL.url = e.target.selectedItem.data;
					loadSound();
				}
			});
		}
		
		private function playNext(e:MouseEvent):void
		{
			trace(e.target);
			if (isPlaying)
			{
				disableVolumeControls();
				Tweener.addTween(soundControl, { _sound_volume:0, time:.5, transition:"linear", onComplete:
					function():void
					{
						// Stop current sound
						soundControl.stop();
						isPlaying = false;
						
						//null the sound instance
						sound = null;
						
						if (trackList.selectedIndex < (trackList.length - 1))
						{
							// select next item of the list
							trackList.selectedItem = trackList.getItemAt(trackList.selectedIndex + 1);
						}
						else
						{
							trackList.selectedItem = trackList.getItemAt(0);
						}
						
						soundURL.url = trackList.selectedItem.data;
						loadSound();
					}
				});
			}
		}
		
		private function playPrev(e:MouseEvent):void
		{
			trace(e.target);
			if (isPlaying)
			{
				disableVolumeControls();
				Tweener.addTween(soundControl, { _sound_volume:0, time:.5, transition:"linear", onComplete:
					function():void
					{
						// Stop current sound
						soundControl.stop();
						isPlaying = false;
						
						//null the sound instance
						sound = null;
						
						if (trackList.selectedIndex > 0)
						{
							// select next item of the list
							trackList.selectedItem = trackList.getItemAt(trackList.selectedIndex - 1);
						}
						else
						{
							trackList.selectedItem = trackList.getItemAt(trackList.length - 1);
						}
						
						soundURL.url = trackList.selectedItem.data;
						loadSound();
					}
				});
			}
		}
		
		private var preloader:Preloader = new Preloader();	
		
		private function showPreloader():void
		{
			if (!this.contains(preloader))
			{
				timerLabel.alpha = 0;
				preloader.alpha = 1;
				preloader.x = this.width - 80;
				preloader.y = volumeLabel.y + 7;
				addChild(preloader);
			}
		}
		
		private function removePreloader():void
		{
			if (this.contains(preloader))
			{
				Tweener.addTween(preloader, { alpha:0, time:.5, transition:'easeOutSine', onComplete:
					function():void
					{
						timerLabel.alpha = 1;
						removeChild(preloader);
						//addEventListener(Event.ENTER_FRAME, onEnterFrame, false, 0, true);
					}
				});
			}
		}
		
		
		private var minutes:Number;
		private var seconds:Number;
		private function onEnterFrame(e:Event):void
		{
			minutes = Math.floor(soundControl.position / 1000 / 60);
			seconds = Math.floor(soundControl.position / 1000) % 60;
			timerLabel.text = ((minutes < 10) ? '( 0' + minutes : '( ' + minutes) + ':' + ((seconds < 10) ? '0' + seconds + ' )': seconds + ' )');
			
			// render visualization for equalizer
			equalizer.render(e);
		}
		
		//--------------------------------------------------- BUILDING UI -------------------------------------------------------//
		private var playBtn:PlayButton;
		private var pauseBtn:PauseButton;
		private var stopBtn:StopButton;
		private var ffBtn:FFButton;
		private var rewBtn:REWButton;
		private var plusBtn:PlusButton;
		private var minusBtn:MinusButton;
		private var volumeLabel:TextField;
		private var timerLabel:TextField;
		var trackList:TrackList;
		
		private function createButtons():void
		{	
			var spacing:int = 3;
			trackList = new TrackList();
			for (var i:int = 0; i < playerData.item.length(); ++i)
			{
				trackList.addItem( { label: i + 1 + '. ' + playerData.item[i].artist + ' - ' + playerData.item[i].songname, data: playerData.item[i].@path} );
			}
			trackList.addEventListener(Event.CHANGE, onTrackChange, false, 0, true);
			addChild(trackList);
			
			//soundChannel buttons
			pauseBtn = new PauseButton();
			pauseBtn.x = trackList.width + spacing;
			pauseBtn.addEventListener(MouseEvent.MOUSE_UP, pauseSound, false, 0, true);
			addChild(pauseBtn);
			
			playBtn = new PlayButton();
			playBtn.x = pauseBtn.x;
			playBtn.addEventListener(MouseEvent.MOUSE_UP, playSound, false, 0, true);
			
			stopBtn = new StopButton();
			stopBtn.x = playBtn.x + playBtn.width + spacing;
			stopBtn.addEventListener(MouseEvent.MOUSE_UP, stopSound, false, 0, true);
			addChild(stopBtn);
			
			//ff & rew buttons
			ffBtn = new FFButton();
			ffBtn.x = stopBtn.x;
			ffBtn.y = stopBtn.y - ffBtn.height - 2;
			ffBtn.addEventListener(MouseEvent.MOUSE_UP, playNext, false, 0, true);
			addChild(ffBtn);
			
			rewBtn = new REWButton();
			rewBtn.x = pauseBtn.x;
			rewBtn.y = pauseBtn.y - rewBtn.height - 2;
			rewBtn.addEventListener(MouseEvent.MOUSE_UP, playPrev, false, 0, true);
			addChild(rewBtn);
			
			//soundTransform buttons & label
			plusBtn = new PlusButton();
			plusBtn.x = equalizer.width + 5;
			plusBtn.y = equalizer.y + 2;
			plusBtn.addEventListener(MouseEvent.MOUSE_UP, setVolume, false, 0, true);
			plusBtn.visible = false;
			plusBtn.alpha = 0;
			addChild(plusBtn);
			
			minusBtn = new MinusButton();
			minusBtn.x = plusBtn.x + plusBtn.width + spacing;
			minusBtn.y = plusBtn.y;
			minusBtn.addEventListener(MouseEvent.MOUSE_UP, setVolume, false, 0, true);
			minusBtn.visible = false;
			minusBtn.alpha = 0;
			addChild(minusBtn);
			
			var blur:BlurFilter = new BlurFilter(0, 0, 1);
			volumeLabel = new TextField();
			volumeLabel.filters = [blur];
			volumeLabel.defaultTextFormat = new TrackTextFormat();
			volumeLabel.embedFonts = true;
			volumeLabel.antiAliasType = AntiAliasType.ADVANCED;
			volumeLabel.autoSize = TextFieldAutoSize.LEFT;
			volumeLabel.selectable = false;
			volumeLabel.mouseEnabled = false;
			volumeLabel.x = minusBtn.x + minusBtn.width + spacing + 10;
			volumeLabel.y = plusBtn.y;
			volumeLabel.alpha = 0;
			addChild(volumeLabel);
			
			timerLabel = new TextField();
			timerLabel.filters = [blur];
			timerLabel.defaultTextFormat = new TrackTextFormat();
			timerLabel.embedFonts = true;
			timerLabel.antiAliasType = AntiAliasType.ADVANCED;
			timerLabel.autoSize = TextFieldAutoSize.LEFT;
			timerLabel.selectable = false;
			timerLabel.mouseEnabled = false;
			timerLabel.x = this.width - 90;
			timerLabel.y = plusBtn.y;
			timerLabel.alpha = 0;
			addChild(timerLabel);
			
			//trace(this.width + ',' + this.height);
			var rect:DrawRectangle = new DrawRectangle(this.width + 20, this.height + 20, 0x000000, .7, 0, 15);
			rect.x -= 10; 
			rect.y -= 110;
			addChildAt(rect, 0);
		}
		
		//--------------------------------------------------- VOLUME MANAGEMENT ------------------------------------------------//
		private function setVolume(e:MouseEvent):void
		{
			trace('Volume before change: ' + transf.volume);
			if (isPlaying)
			{
				if (e.target != null)
				{
					if (e.target is PlusButton && transf.volume < 1)
					{
						trace('plus hit');
						transf.volume += .1;
						transf.volume = Math.ceil(transf.volume * 10) / 10;
						Tweener.addTween(soundControl, { _sound_volume:transf.volume, time:.05, transition:'linear' } );						
					}
					else if (e.target is MinusButton  && transf.volume > 0)
					{
						trace('minus hit');
						transf.volume -= .1;
						transf.volume = Math.floor(transf.volume * 10) / 10;
						Tweener.addTween(soundControl, { _sound_volume:transf.volume, time:.05, transition:'linear' } );
					}
				}
				trace('Volume after change: ' + transf.volume);
				volumeLabel.text = 'Volume: ' + transf.volume * 100 + ' %';
			}
		}
		
		private function enableVolumeControls():void
		{
			//set text for volume label & make volume buttons visible
			volumeLabel.text = 'Volume: ' + (soundControl.soundTransform.volume * 100) + ' %';
			Tweener.addTween(volumeLabel, { alpha:1, time:.5, transition:'easeOutSine'} );
			
			plusBtn.visible = true;
			minusBtn.visible = true;
			volumeLabel.visible = true;
			Tweener.addTween(plusBtn, { alpha:1, time:.5, transition:'easeOutSine' } );
			Tweener.addTween(minusBtn, { alpha:1, time:.5, transition:'easeOutSine'} );
		}
		
		private function disableVolumeControls():void
		{
			Tweener.addTween(volumeLabel, { alpha:0, time:.5, transition:'easeOutSine', onComplete:function():void { this.visible = false; }} );
			Tweener.addTween(plusBtn, { alpha:0, time:.5, transition:'easeOutSine', onComplete:function():void { this.visible = false; } } );
			Tweener.addTween(minusBtn, { alpha:0, time:.5, transition:'easeOutSine', onComplete:function():void { this.visible = false; }} );
		}
		
		
		//------------------------------------------------SOUND PLAYBACK FUNCTIONS-------------------------------------------------//
		public function pauseSound(e:MouseEvent = null):void
		{
			// if sound isPlaying --> pause it, display pause button
			if (isPlaying)
			{
				isPlaying = false;
				curTransform = transf;
				//trace('Pausing sound \t Cur sound volume: ' + soundControl.soundTransform.volume);
				
				Tweener.addTween(soundControl, { _sound_volume:0, time:.5, transition:"linear", onComplete:
					function():void
					{
						removeEventListener(Event.ENTER_FRAME, onEnterFrame);
						resumeTime = soundControl.position;
						soundControl.stop();
						volumeLabel.text = 'Volume: 0 %';
					}
				} );
				
				//remove button from stage
				removeChild(pauseBtn);
				addChild(playBtn);
			}
		}
		
		public function playSound(e:MouseEvent = null):void
		{
			
			// resume plaback 
			if (!isPlaying)
			{
				ExternalInterface.call("setPlayer" , "main");
				
				isPlaying = true;
				soundControl = sound.play(resumeTime);
				soundControl.soundTransform = curTransform;
				//trace('Playing : ' + curSoundVolume);
				volumeLabel.text = 'Volume: ' + (curTransform.volume * 100) + ' %';
				//remove button from stage
				removeChild(PlayButton(e.target));
				addChild(pauseBtn);
				
				if (!hasEventListener(Event.ENTER_FRAME))
				{
					addEventListener(Event.ENTER_FRAME, onEnterFrame, false, 0, true);
				}
			}
		}
		
		public function stopSound(e:MouseEvent = null):void
		{
			// stop playback
			if (isPlaying)
			{
				isPlaying = false;
				curTransform = transf;
				Tweener.addTween(soundControl, { _sound_volume:0, time:.5, transition:"linear", onComplete:
					function():void
					{
						removeEventListener(Event.ENTER_FRAME, onEnterFrame);
						soundControl.stop();
						// reset resumeTime
						resumeTime = 0;
						volumeLabel.text = 'Volume: 0 %';
						timerLabel.text = '( 00:00 )';
					}
				});
				
				if (this.contains(pauseBtn))
				{
					removeChild(pauseBtn);
					addChild(playBtn);
				}
			}
		}
	}
}


